from od.object import Object
import time
from car.car_timer import CarTimer
from car.base import Base
import jetson.inference
import jetson.utils

class RecognitionSingle(Base):
    """
    该类用于初始化，并启动系统的对象检测进程，返回检测结果。该类使用单线程。
    此类应该实现单例较为合理。等以后有机会再改。系统必须安装v4l-utils，才能支持USB摄像头
    帧率受到系统性能和摄像头本身帧速度的限制。
    """
    def __init__(self, device="/dev/video0", width=320, height=240, display_window=True, threshold=0.5,
                 rt_model=None, rt_labels=None):
        """
            初始化识别类，此处应注意，如果指定摄像头的宽和高摄像头本身不支持，必定会出现错误。
            以下语句可以检测摄像头的分辨率：v4l2-ctl --list-formats-ext
        :param device: 指定摄像头（/dev/video?）
        :param width: 指定摄像头宽度
        :param height: 指定摄像头高度
        :param display_window:是否开始监视窗口，默认是
        :param threshold： 模型推理的阈值
        :param rt_model: 自定义模型路径
        :param rt_labels:自定义标签路径
        """
        super().__init__()
        self.__timer = CarTimer()
        self.objects_list = []
        if rt_model is not None and rt_labels is not None:
            self.__model = "--model=" + rt_model
            self.__labels = "--labels=" + rt_labels
            self.net = jetson.inference.detectNet(
                argv=[self.__model,
                      self.__labels,
                      "--input-blob=input_0", "--output-cvg=scores", "--output-bbox=boxes"], threshold=threshold)
        else:
            self.net = jetson.inference.detectNet("ssd-mobilenet-v2", threshold)
        self.camera = jetson.utils.gstCamera(width, height, device)
        self.display = jetson.utils.videoOutput("display://0")  # 'my_video.mp4' for file

    def get_objects(self):
        """
            在循环中不停的调用本函数来刷新识别到的物体，当刷新速率超过设定的识别帧率（frequency）时，会返回一个空的列表（list）
        :return: 返回一个包含Object对象的列表。
        """
        img, _, _ = self.camera.CaptureRGBA()
        # img = self.camera.Capture()
        detections = self.net.Detect(img)
        self.display.Render(img)
        self.display.SetStatus("Object Detection | Network {:.0f} FPS".format(self.net.GetNetworkFPS()))
        self.objects_list = []
        if len(detections) > 0:
            detections_list = []
            for d in detections:
                detections_list.append(
                        [d.ClassID, d.Confidence, d.Left, d.Right, d.Top, d.Bottom, d.Area, d.Center])
            self.objects_list = Object.get_list(detections_list)
        return self.objects_list

    def object_appeared(self, appeared_id, object_width_threshold=60, delay_time=10):
        """
            根据输入的目标ID，目标宽度，延迟时间 检测所需目标是否出现并符合设定条件。
            目标ID根据COCO数据集定义。object_appeared用于协助检测对象是否出现。
        :param appeared_id:需要检测的目标ID
        :param object_width_threshold:目标的宽度是否超过阈值
        :param delay_time:两次检测时间间隔（避免重复检测到同一对象）
        :return:当目标对象出现并符合设定的条件返回TRUE，否则返回FALSE
        """

        self.__timer.interval = delay_time
        if self.__timer.timeout():
            if self.objects_list:
                for obj in self.objects_list:
                    if obj.class_id == appeared_id and obj.width > object_width_threshold:
                        self.__timer.start_time = time.perf_counter()
                        return True
        return False

    def close(self):
        """
        关闭子线程，必须显式关闭，否则识别子进程将不会自动退出。
        """
        pass

    def execute(self, frame, render_frame_list):
        """
        用于事件的触发和返回
        """
        tmp_list = self.get_objects()
        if tmp_list:
            if not (self.event_function is None):
                self.event_function(objects_list=tmp_list)
